home *** CD-ROM | disk | FTP | other *** search
- IDEAL
- ModeL large
-
- ;+---------------------------------------------------------------------------+
- ;| IBM-PC(tm) compatible programmer's DMA library |
- ;| Version 1.1 |
- ;+---------------------------------------------------------------------------+
- ;| Copyright (C) 1992, Heath I Hunnicutt |
- ;+---------------------------------------------------------------------------+
- ;| Thanks to: Gary Nemirovsky |
- ;+---------------------------------------------------------------------------+
- ;| This document is for free public distribution. It is unlawful to |
- ;| sell this document, or any work based substantially upon it. |
- ;+---------------------------------------------------------------------------+
- ;| This assembly code defines 3 functions that are intended for use |
- ;| by C programmers in code that requires access to the DMA system. |
- ;| |
- ;| DMA transfers occur asynchronously to the CPU's activity, so they |
- ;| are quite efficient. |
- ;| |
- ;| The general sequence for using the DMA is: |
- ;| int channel=1; |
- ;| if (dma_reset(channel)) |
- ;| abort(); |
- ;| if (dma_setup(channel,(char far *)My_Buffer,sizeof(My_Buffer),1)) |
- ;| abort(); |
- ;| /* Insert "foreground" code here. */ |
- ;| while (dma_done(channel)!=-1) { |
- ;| if (dma_errno) |
- ;| abort(); |
- ;| } |
- ;+---------------------------------------------------------------------------+
- ;| Send suggestions, questions, comments, knoweledge to: |
- ;| heathh@cco.caltech.edu (also @tybalt.cco.caltech.edu) |
- ;| (or hihunn@through.ugcs.caltech.edu -- not preferred) |
- ;+---------------------------------------------------------------------------+
- ;| PUBLIC FUNCTIONS |
- ;| int far dma_reset(int Channel) |
- ;| int far dma_setup(int Channel,char far *Buffer,unsigned Length,int Dir) |
- ;| int far dma_done(int Channel) |
- ;+---------------------------------------------------------------------------+
- ;| PUBLIC DATA |
- ;| int far dma_errno |
- ;| char far *dma_errlist[] |
- ;+---------------------------------------------------------------------------+
- ;| How to assemble this code: |
- ;| You'll need Turbo Assembler(tm) from Borland(tm) Internationl |
- ;| TASM /mx /m2 dma_code |
- ;+---------------------------------------------------------------------------+
- ;| HISTORY: |
- ;| Ver 1.0 - Initial Release |
- ;| Ver 1.1 - Error checking and reporting added to all functions |
- ;| dma_setup(..) should never crash your system now. |
- ;+---------------------------------------------------------------------------+
-
- Status EQU 08h ;DMAC status port (read) \ same port
- Command EQU 08h ;DMAC command port (write) / (read/write)
- ;STATUS/COMMAND BYTE: ("*" represents defaults)
- ; [ 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
- ; Bit 0: Memory-to-memory transfer 0 => disable*
- ; 1 => enable
- ; 1: "Don't Care" if mem-to-mem disabled (Bit 0==0)*
- ; Channel 0 address hold 0 => disable
- ; 1 => enable
- ; 2: Controller enable 0 => enable*
- ; 1 => disable
- ; 3: "Don't Care" if mem-to-mem enabled (Bit 0==1)
- ; Timing 0 => Normal?
- ; 1 => Compressed?
- ; 4: Priority 0 => Fixed?
- ; 1 => Rotating
- ; 5: "Don't care" if compressed timing (Bit 3==1)
- ; Write selection 0 => Late
- ; 1 => Extended
- ; 6: DREQ sense active 0 => High
- ; 1 => Low
- ; 7: DACK sense active 0 => Low
- ; 1 => High
-
- Request EQU 09h ;DMAC channel request (write-only)
- ;REQUEST BYTE:
- ; [ 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
- ; \__________________/ | \_____/
- ; Don't care | |
- ; | +------+ 00 = Select channel 0
- ; | | 01 = Select channel 1
- ; | | 10 = Select channel 2
- ; | + 11 = Select channel 3
- ; +---+ 0 = Reset request bit
- ; + 1 = Set request bit
-
- DMA_Mask EQU 0Ah ;DMAC DMA_Mask (write-only)
- Mode EQU 0Bh ;DMAC mode (read/write)
-
-
- byte_ptr EQU 00ch ; byte pointer flip-flop
-
- addr EQU 000h ; per-channel base address
- count EQU 001h ; per-channel byte count
-
- read_cmd EQU 048h ; read mode
- write_cmd EQU 044h ; write mode
- set_cmd EQU 000h ; DMA_Mask set
- reset_cmd EQU 004h ; DMA_Mask reset
-
- ; dma controller page register table
- ; this table maps from channel number to the i/o port number of the
- ; page register for that channel
- DATASEG
-
- page_table DW 00087h ; channel 0
- DW 00083h ; channel 1
- DW 00081h ; channel 2
- DW 00082h ; channel 3
-
- ; "Extra" messages are for future compatability with the Virtual DMA
- ; specification.
- DMA_E0 DB 0
- DMA_E1 DB "Region not in contiguous memory.",0
- DMA_E2 DB "Region crossed a physical alignment boundary.",0
- DMA_E3 DB "Unable to lock pages.",0
- DMA_E4 DB "No buffer available.",0
- DMA_E5 DB "Region too large for buffer.",0
- DMA_E6 DB "Buffer currently in use.",0
- DMA_E7 DB "Invalid memory region.",0
- DMA_E8 DB "Region was not locked.",0
- DMA_E9 DB "Number of physical pages greater than table length.",0
- DMA_EA DB "Ivalid buffer ID.",0
- DMA_EB DB "Copy out of buffer range.",0
- DMA_EC DB "Invalid DMA channel number.",0
- _dma_errlist DD DMA_E0, DMA_E1, DMA_E2, DMA_E3, DMA_E4, DMA_E5, DMA_E6, DMA_E7, DMA_E8, DMA_E9, DMA_EA, DMA_EB, DMA_EC
- _dma_errno DW 0
-
- ;char far *dma_errlist[]
- ;int _dma_errno
- PUBLIC _dma_errlist,_dma_errno
-
- CODESEG
- MACRO zero reg
- xor reg,reg
- ENDM zero
-
- PUBLIC _dma_setup,_dma_reset,_dma_done
- ;+---------------------------------------------------------------------------+
- ;| int far dma_setup(int Channel,char far *Buffer,unsigned Length,int Dir) |
- ;| ------------------------------------------------------------------------- |
- ;| Channel = 0-3 !Channel 0 is often reserved for memory refresh! |
- ;| Buffer = Address of data to transfer |
- ;| Length = Length of data to transfer |
- ;| Dir = Direction to move bytes. 1 == Out to the BUS (TO the card) |
- ;| 0 == In from the BUS and cards. |
- ;| ------------------------------------------------------------------------- |
- ;| Returns: 0 if no errors (dma_errno == 0) |
- ;| -1 if errors occured (dma_errno set to indicate error.) |
- ;+---------------------------------------------------------------------------+
- PROC _dma_setup FAR
- ARG Channel:WORD,Buffer:DWORD,Len:WORD,Dir:WORD
- push bp
- mov bp,sp
- push bx cx dx si di
- pushf
-
- mov [_dma_errno],0
- ;Convert seg:ofs Buffer to 20-bit physical address
- ;Assumes operating in 8086/real-Mode
- mov bx,[WORD PTR Buffer]
- mov ax,[WORD PTR Buffer+2]
- mov cl,4
- rol ax,cl
- mov ch,al
- and al,0F0h
- add ax,bx
- adc ch,0
- and ch,0Fh
- mov di,ax
- ; (ch << 16) + di == The physical buffer base.
-
- ;Calculate the port to receive this address
- mov bx,[Channel]
- cmp bx,3
- jbe @@OkChannel
- mov [_dma_errno],0Ch
- mov ax,-1
- jmp @@ExitPt
- @@OkChannel:
- shl bx,1
- ;bx == Port # Channel*2
-
- ;Determine which command byte will be written later
- cmp [WORD PTR Dir],0
- jnz SHORT @@Do_Read
- mov al,write_cmd
- jmp SHORT @@Do_Mode
- @@Do_Read:
- mov al,read_cmd
- @@Do_Mode:
- push cx
- mov cx,[Channel]
- add al,cl
- zero ah
- mov si,ax
- mov ax,set_cmd
- add al,cl
- pop cx
- mov cl,al
- ;si contains READ/WRITE command for DMA controller
- ;cl contains confirmation command for DMA controller
-
- ;-------------------------------------------------------------------------
- ; Calculations have been done ahead of time to minimize time with
- ; interrupts disabled.
- ;
- ; ch:di == physical base address
- ;
- ; cl == Confirmation command (Tells DMA we're done bothering it.)
- ;
- ; bx == I/O port Channel*2 (This is where the address is written)
- ;
- ; si == Mode command for DMA
- ;-------------------------------------------------------------------------
- mov ax,di ;Let's check the address to see if we
- add ax,[Len] ;span a page boundary with our length
- jnc @@BoundaryOk ;Do we?
- mov [_dma_errno],2 ; y: Error #2
- mov ax,-1 ; Return -1
- jmp @@ExitPt ; See ya...
- @@BoundaryOk: ; n: Continue with action
- cli ;Disable interrupts while mucking with DMA
-
- ;The "byte pointer" is also known as the LSB/MSB flip flop.
- ;By writing any value to it, the DMA controller registers are prepared
- ;to accept the address and length values LSB first.
- mov dx,byte_ptr ;Reset byte pointer Flip/flop
- out dx,al ;All we have to do is write to it
-
- mov ax,di ;ax=LSW of 20-bit address
- mov dx,bx ;dx=DMAC Base Address port
- out dx,al ;Store LSB
- mov al,ah
- out dx,al ;Store next byte
-
- mov al,ch ;al=Page number
- mov dx,[bx + OFFSET page_table] ;dx=Port is the "Page index"
- out dx,al ;Store the page
-
- ;Write length to port Channel*2 + 1
- mov ax,[Len]
- mov dx,bx ;dx=DMAC Base Adress port
- inc dx ;dx=DMAC Count port (1 after Base address)
- out dx,al ;Write LSB of Length
- mov al,ah
- out dx,al ;Write MSB
-
- mov ax,si ;Load pre-calculated mode
- mov dx,Mode ;dx=DMAC mode register
- out dx,al ;Write it to the DSP
-
- mov dx,DMA_Mask ;dx=DMAX DMA_Mask register
- mov al,cl ;al=pre-calulated DMA_Mask value
- out dx,al ;Write DMA_Mask
- mov ax,0 ;Return with no error
-
- @@ExitPt: ;Restore stack and return
- popf
- pop di si dx cx bx
- pop bp
- ret
- ENDP _dma_setup
-
- ;+---------------------------------------------------------------------------+
- ;| int far dma_reset(int Channel) |
- ;| ------------------------------------------------------------------------- |
- ;| Channel = 0-3 |
- ;| Resets the specified channel. |
- ;| ------------------------------------------------------------------------- |
- ;| Returns 0 if Ok, -1 and sets dma_errno on error |
- ;+---------------------------------------------------------------------------+
- PROC _dma_reset FAR
- ARG Channel:Word
- push bp
- mov bp,sp
- push dx
- mov [_dma_errno],0
- cmp [Channel],3
- jbe @@OkChannel
- mov [_dma_errno],0Ch
- mov ax,-1
- jmp @@Exit_Pt
- @@OkChannel:
- mov dx,DMA_Mask
- mov ax,reset_cmd
- add ax,[Channel]
- out dx,al
- mov ax,0
- @@Exit_Pt:
- pop dx
- pop bp
- ret
- ENDP _dma_reset
-
- ;+---------------------------------------------------------------------------+
- ;| int far dma_done(Channel) |
- ;| ------------------------------------------------------------------------- |
- ;| Channel = 0-4 |
- ;| ------------------------------------------------------------------------- |
- ;| Returns: -1 if DMA transaction completed |
- ;| (Maybe it returns the number of bytes left to transfer?) |
- ;| dma_errno == 0 if no error, otherwise equals error number |
- ;+---------------------------------------------------------------------------+
- PROC _dma_done FAR
- ARG Channel:Word
- push bp
- mov bp,sp
- pushf
- push dx
- cmp [Channel],3
- jbe @@OkChannel
- mov ax,-1
- mov [_dma_errno],0Ch
- jmp @@Exit_Pt
- @@OkChannel:
- mov dx,[Channel]
- shl dx,1
- add dx,count
- cli
- in al,dx
- mov ah,al
- in al,dx
- xchg al,ah
- @@Exit_Pt:
- pop dx
- popf
- pop bp
- ret
- ENDP _dma_done
- END
-